/*
 *
 * Copyright (C) 2012 Walter Lütgenau
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
package offset.nodes.client.virtual.model.jcr.nodetype;

import java.io.InputStream;
import java.util.Collection;
import java.util.LinkedList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.jcr.RepositoryException;
import javax.jcr.Value;
import javax.jcr.ValueFormatException;
import javax.xml.parsers.SAXParserFactory;
import offset.nodes.client.model.DummyContentHandler;
import offset.nodes.client.model.SimpleNamespaceRegistry;
import offset.nodes.client.virtual.model.jcr.QName;
import offset.nodes.client.virtual.model.jcr.SimpleValue;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

/**
 *
 * @author Walter Lütgenau
 */
public class NodeTypeReader extends DummyContentHandler implements ContentHandler {

    NodeTypeDefinition nodeType;
    NodeDefinition child = null;
    PropertyDefinition property = null;
    StringBuffer value = new StringBuffer();
    Collection<NodeTypeDefinition> nodeTypes = new LinkedList<NodeTypeDefinition>();
    public static final String TRUE = "true";

    public Collection<NodeTypeDefinition> getNodeTypes() {
        return nodeTypes;
    }

    public static NodeTypeDefinition[] read(InputStream xml) throws Exception {
        NodeTypeReader typeReader = new NodeTypeReader();

        XMLReader reader = SAXParserFactory.newInstance().newSAXParser().getXMLReader();
        reader.setContentHandler(typeReader);

        reader.parse(new InputSource(xml));

        return typeReader.getNodeTypes().toArray(new NodeTypeDefinition[typeReader.getNodeTypes().size()]);
    }

    @Override
    public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
        if (getName(uri, localName, qName).equals("nodeType"))
            addNodeTypeDefinition(atts);
        else if (getName(uri, localName, qName).equals("propertyDefinition"))
            addPropertyDefinition(atts);
        else if (getName(uri, localName, qName).equals("childNodeDefinition"))
            addChildNodeDefinition(atts);

    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        if (getName(uri, localName, qName).equals("nodeType"))
            nodeTypes.add(nodeType);
        else if (getName(uri, localName, qName).equals("supertype"))
            addSuperType();
        else if (getName(uri, localName, qName).equals("requiredPrimaryType"))
            addRequiredPrimaryType();
        else if (getName(uri, localName, qName).equals("valueConstraint"))
            addValueConstraintDefinition();
        else if (getName(uri, localName, qName).equals("defaultValue"))
            addDefaultValueDefinition();
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        value.append(ch, start, length);
    }

    protected String getValue() {
        String result = value.toString();
        value = new StringBuffer();

        return result;
    }

    protected String getName(String uri, String localName, String qName) {
        return qName;
    }

    protected void addSuperType() {
        QName[] superTypes = nodeType.getSupertypes();

        QName[] result = new QName[superTypes.length + 1];
        System.arraycopy(superTypes, 0, result, 0, superTypes.length);
        result[result.length - 1] = QName.valueOf(SimpleNamespaceRegistry.getInstance().toQNameString(getValue()));
        value = new StringBuffer();

        nodeType.setSupertypes(result);
    }

    protected void addRequiredPrimaryType() {
        QName[] types = child.getRequiredPrimaryTypes();

        QName[] result = new QName[types.length + 1];
        System.arraycopy(types, 0, result, 0, types.length);
        result[result.length - 1] = QName.valueOf(SimpleNamespaceRegistry.getInstance().toQNameString(getValue()));

        child.setRequiredPrimaryTypes(result);
    }

    protected void addPropertyDefinition(Attributes atts) {
        PropertyDefinition definition = new PropertyDefinition();

        for (int i = 0; i < atts.getLength(); i++) {
            if (getName(atts.getURI(i), atts.getLocalName(i), atts.getQName(i)).equals("autoCreated"))
                definition.setAutoCreated(atts.getValue(i).equals(TRUE));
            else if (getName(atts.getURI(i), atts.getLocalName(i), atts.getQName(i)).equals("mandatory"))
                definition.setMandatory(atts.getValue(i).equals(TRUE));
            else if (getName(atts.getURI(i), atts.getLocalName(i), atts.getQName(i)).equals("multiple"))
                definition.setMultiple(atts.getValue(i).equals(TRUE));
            else if (getName(atts.getURI(i), atts.getLocalName(i), atts.getQName(i)).equals("protected"))
                definition.setProtected(atts.getValue(i).equals(TRUE));
            else if (getName(atts.getURI(i), atts.getLocalName(i), atts.getQName(i)).equals("name"))
                definition.setName(QName.valueOf(SimpleNamespaceRegistry.getInstance().toQNameString(atts.getValue(i))));
            else if (getName(atts.getURI(i), atts.getLocalName(i), atts.getQName(i)).equals("onParentVersion"))
                definition.setOnParentVersion(definition.parentVersion(atts.getValue(i)));
            else if (getName(atts.getURI(i), atts.getLocalName(i), atts.getQName(i)).equals("requiredType"))
                definition.setRequiredType(definition.type(atts.getValue(i)));
        }

        PropertyDefinition[] definitions = nodeType.getDeclaredPropertyDefinitions();

        PropertyDefinition[] result = new PropertyDefinition[definitions.length + 1];
        System.arraycopy(definitions, 0, result, 0, definitions.length);
        result[result.length - 1] = definition;
        property = definition;

        nodeType.setPropertyDefs(result);
    }

    protected void addValueConstraintDefinition() {
        ValueConstraintDefinition[] valueConstraints = property.getValueConstraints();
        ValueConstraintDefinition valueConstraint = new ValueConstraintDefinition(getValue());

        ValueConstraintDefinition[] result = new ValueConstraintDefinition[valueConstraints.length + 1];
        System.arraycopy(valueConstraints, 0, result, 0, valueConstraints.length);
        result[result.length - 1] = valueConstraint;

        property.setValueConstraints(result);
    }

    protected void addDefaultValueDefinition() throws SAXException {
        try {
            Value[] defaultValues = property.getDefaultValues();
            if (defaultValues == null)
                defaultValues = new Value[0];
            SimpleValue defaultValue = new SimpleValue(getValue());
            SimpleValue[] result = new SimpleValue[defaultValues.length + 1];
            for (int i = 0; i < defaultValues.length; i++) {
                result[i] = new SimpleValue(defaultValues[i].getString());
            }
            result[result.length - 1] = defaultValue;
            property.setDefaultValues(result);
        } catch (Exception ex) {
            throw new SAXException(ex);
        }
    }

    protected void addChildNodeDefinition(Attributes atts) {
        child = new NodeDefinition();

        for (int i = 0; i < atts.getLength(); i++) {
            if (getName(atts.getURI(i), atts.getLocalName(i), atts.getQName(i)).equals("autoCreated"))
                child.setAutoCreated(atts.getValue(i).equals(TRUE));
            else if (getName(atts.getURI(i), atts.getLocalName(i), atts.getQName(i)).equals("mandatory"))
                child.setMandatory(atts.getValue(i).equals(TRUE));
            else if (getName(atts.getURI(i), atts.getLocalName(i), atts.getQName(i)).equals("sameNameSiblings"))
                child.setAllowsSameNameSiblings(atts.getValue(i).equals(TRUE));
            else if (getName(atts.getURI(i), atts.getLocalName(i), atts.getQName(i)).equals("protected"))
                child.setProtected(atts.getValue(i).equals(TRUE));
            else if (getName(atts.getURI(i), atts.getLocalName(i), atts.getQName(i)).equals("name"))
                child.setName(QName.valueOf(SimpleNamespaceRegistry.getInstance().toQNameString(atts.getValue(i))));
            else if (getName(atts.getURI(i), atts.getLocalName(i), atts.getQName(i)).equals("onParentVersion"))
                child.setOnParentVersion(child.parentVersion(atts.getValue(i)));
            else if (getName(atts.getURI(i), atts.getLocalName(i), atts.getQName(i)).equals("defaultPrimaryType"))
                child.setDefaultPrimaryType(QName.valueOf(SimpleNamespaceRegistry.getInstance().toQNameString(atts.getValue(i))));
        }

        NodeDefinition[] definitions = nodeType.getDeclaredChildNodeDefinitions();

        NodeDefinition[] result = new NodeDefinition[definitions.length + 1];
        System.arraycopy(definitions, 0, result, 0, definitions.length);
        result[result.length - 1] = child;

        nodeType.setChildNodeDefs(result);
    }

    protected void addNodeTypeDefinition(Attributes atts) {
        NodeTypeDefinition definition = new NodeTypeDefinition();

        for (int i = 0; i < atts.getLength(); i++) {
            if (getName(atts.getURI(i), atts.getLocalName(i), atts.getQName(i)).equals("hasOrderableChildNodes"))
                definition.setOrderableChildNodes(atts.getValue(i).equals(TRUE));
            else if (getName(atts.getURI(i), atts.getLocalName(i), atts.getQName(i)).equals("isMixin"))
                definition.setMixin(atts.getValue(i).equals(TRUE));
            else if (getName(atts.getURI(i), atts.getLocalName(i), atts.getQName(i)).equals("primaryItemName"))
                definition.setPrimaryItemName(QName.valueOf(SimpleNamespaceRegistry.getInstance().toQNameString(atts.getValue(i))));
            else if (getName(atts.getURI(i), atts.getLocalName(i), atts.getQName(i)).equals("name"))
                definition.setName(QName.valueOf(SimpleNamespaceRegistry.getInstance().toQNameString(atts.getValue(i))));
        }

        nodeType = definition;
    }
}
